package com.atguigu.tingshu.search.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.extra.pinyin.PinyinUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import co.elastic.clients.elasticsearch._types.aggregations.Buckets;
import co.elastic.clients.elasticsearch._types.aggregations.LongTermsAggregate;
import co.elastic.clients.elasticsearch._types.aggregations.LongTermsBucket;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.NestedQuery;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.CompletionSuggestOption;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.Suggestion;
import co.elastic.clients.elasticsearch.ml.ValidateRequest;
import co.elastic.clients.json.JsonData;
import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.model.album.*;
import com.atguigu.tingshu.model.search.AlbumInfoIndex;
import com.atguigu.tingshu.model.search.AttributeValueIndex;
import com.atguigu.tingshu.model.search.SuggestIndex;
import com.atguigu.tingshu.query.search.AlbumIndexQuery;
import com.atguigu.tingshu.search.repository.AlbumInfoIndexRepository;
import com.atguigu.tingshu.search.repository.SuggestIndexRepository;
import com.atguigu.tingshu.search.service.SearchService;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexVo;
import com.atguigu.tingshu.vo.search.AlbumSearchResponseVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import jakarta.json.JsonValue;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.suggest.Completion;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;


@Slf4j
@Service
@SuppressWarnings({"all"})
public class SearchServiceImpl implements SearchService {


    @Autowired
    private AlbumFeignClient albumFeignClient;


    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private AlbumInfoIndexRepository albumInfoIndexRepository;

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;

    @Autowired
    private ElasticsearchClient elasticsearchClient;

    @Autowired
    private SuggestIndexRepository suggestIndexRepository;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 索引库名称
     */
    private static final String INDEX_NAME = "albuminfo";

    /**
     * 封装索引库文档对象，将指定已存在专辑上架保存到索引库
     *
     * @param albumId
     */
    @Override
    public void upperAlbum(Long albumId) {
        AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex();
        //1.远程调用专辑服务-根据专辑ID查询专辑信息（包含专辑标签列表）
        CompletableFuture<AlbumInfo> albumInfoCompletableFuture = CompletableFuture.supplyAsync(() -> {
            AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(albumId).getData();
            if (albumInfo == null) {
                return null;
            }
            //1.1 将专辑基本信息拷贝到专辑索引库对象中
            BeanUtil.copyProperties(albumInfo, albumInfoIndex);
            //1.2 封装专辑标签列表
            List<AlbumAttributeValue> albumAttributeValueVoList = albumInfo.getAlbumAttributeValueVoList();
            if (CollectionUtil.isNotEmpty(albumAttributeValueVoList)) {
                //将集合泛型AlbumAttributeValue转为AttributeValueIndex类型
                List<AttributeValueIndex> attributeValueIndexList = albumAttributeValueVoList.stream().map(albumAttributeValue -> {
                    return BeanUtil.copyProperties(albumAttributeValue, AttributeValueIndex.class);
                }).collect(Collectors.toList());
                albumInfoIndex.setAttributeValueIndexList(attributeValueIndexList);
            }
            return albumInfo;
        }, threadPoolExecutor);

        //2.远程调用专辑服务-获取专辑所属三级分类信息
        CompletableFuture<Void> categoryCompletableFuture = albumInfoCompletableFuture.thenAcceptAsync(albumInfo -> {
            BaseCategoryView baseCategoryView = albumFeignClient.getCategoryView(albumInfo.getCategory3Id()).getData();
            if (baseCategoryView != null) {
                albumInfoIndex.setCategory1Id(baseCategoryView.getCategory1Id());
                albumInfoIndex.setCategory2Id(baseCategoryView.getCategory2Id());
                albumInfoIndex.setCategory3Id(baseCategoryView.getCategory3Id());
            }
        }, threadPoolExecutor);

        //3.远程调用用户服务-获取主播名称
        CompletableFuture<Void> userInfoCompletableFuture = albumInfoCompletableFuture.thenAcceptAsync(albumInfo -> {
            UserInfoVo userInfoVo = userFeignClient.getUserInfoVoById(albumInfo.getUserId()).getData();
            if (userInfoVo != null) {
                albumInfoIndex.setAnnouncerName(userInfoVo.getNickname());
            }
        }, threadPoolExecutor);


        //组合多个异步任务对象，要求所有异步任务必须执行完毕
        CompletableFuture.allOf(
                albumInfoCompletableFuture,
                categoryCompletableFuture,
                userInfoCompletableFuture
        ).join();

        //4.专辑统计信息生成随机数-方便后续排序测试 TODO 远程调用专辑服务获取专辑实际统计数值
        //4.1 随机产生4个数值
        int num1 = RandomUtil.randomInt(500, 1000);
        int num2 = RandomUtil.randomInt(300, 500);
        int num3 = RandomUtil.randomInt(10, 100);
        int num4 = RandomUtil.randomInt(100, 300);
        albumInfoIndex.setPlayStatNum(num1);
        albumInfoIndex.setSubscribeStatNum(num2);
        albumInfoIndex.setBuyStatNum(num3);
        albumInfoIndex.setCommentStatNum(num4);
        //4.2 基于随机值计算得分
        BigDecimal bigDecimal1 = BigDecimal.valueOf(num1).multiply(new BigDecimal("0.4"));
        BigDecimal bigDecimal2 = BigDecimal.valueOf(num2).multiply(new BigDecimal("0.3"));
        BigDecimal bigDecimal3 = BigDecimal.valueOf(num3).multiply(new BigDecimal("0.2"));
        BigDecimal bigDecimal4 = BigDecimal.valueOf(num4).multiply(new BigDecimal("0.1"));
        BigDecimal hotScore = bigDecimal1.add(bigDecimal2).add(bigDecimal3).add(bigDecimal4);
        //保留2位小数，四舍五入
        hotScore.setScale(2, RoundingMode.HALF_UP);
        albumInfoIndex.setHotScore(hotScore.doubleValue());


        //5.封装好索引库文档对象：AlbumInfoIndex
        //6.调用专辑索引库持久层对象保存文档
        albumInfoIndexRepository.save(albumInfoIndex);


        //TODO 7.将新增专辑标题作为提词存入提词索引库 用于关键字自动补全
        //7.1 处理专辑标题
        SuggestIndex suggestIndex = new SuggestIndex();
        suggestIndex.setId(albumInfoIndex.getId().toString());
        suggestIndex.setTitle(albumInfoIndex.getAlbumTitle());
        suggestIndex.setKeyword(new Completion(new String[]{albumInfoIndex.getAlbumTitle()}));
        suggestIndex.setKeywordPinyin(new Completion(new String[]{PinyinUtil.getPinyin(albumInfoIndex.getAlbumTitle(), "")}));
        suggestIndex.setKeywordSequence(new Completion(new String[]{PinyinUtil.getFirstLetter(albumInfoIndex.getAlbumTitle(), "")}));

        suggestIndexRepository.save(suggestIndex);


        //8.将上架专辑ID存入布隆过滤器中
        RBloomFilter<Long> bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER);
        bloomFilter.add(albumId);
    }

    /**
     * 仅用于测试-将指定专辑从索引库中删除
     *
     * @param albumId
     * @return
     */
    @Override
    public void lowerAlbum(Long albumId) {
        albumInfoIndexRepository.deleteById(albumId);
    }

    /**
     * 专辑站内检索：支持关键字、分类及标签过滤，分页，高亮
     *
     * @param queryVo
     * @return
     */
    @Override
    public AlbumSearchResponseVo search(AlbumIndexQuery queryVo) {
        try {
            //一、构建检索请求对象（构建完整建设请求路径，请求头参数）
            SearchRequest searchRequest = this.buildDSL(queryVo);
            System.err.println("本次检索DSL：");
            System.err.println(searchRequest.toString());

            //二、调用ES-Restful接口执行检索
            SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);

            //三、解析ES响应结果
            return this.parseResult(searchResponse, queryVo);
        } catch (Exception e) {
            log.error("[搜索服务]关键字检索异常：{}", e);
            throw new RuntimeException(e);
        }
    }


    /**
     * 根据检索入参对象封装检索请求对象
     *
     * @param queryVo 查询条件
     * @return
     */
    @Override
    public SearchRequest buildDSL(AlbumIndexQuery queryVo) {
        //1.构建检索请求对象构建器对象 封装检索索引库名称
        SearchRequest.Builder searchRequestBuilder = new SearchRequest.Builder();
        searchRequestBuilder.index(INDEX_NAME);

        //2.设置查询条件，设置请求体参数中"query" 关键字、分类ID、标签
        //2.0 创建最外层bool查询-设置关键字及过滤条件
        BoolQuery.Builder allBoolQueryBuilder = new BoolQuery.Builder();
        //2.1 设置关键字条件查询 要求：关键字任意匹配专辑标题，简介，主播名称
        String keyword = queryVo.getKeyword();
        if (StringUtils.isNotBlank(keyword)) {
            //2.1.1 创建封装关键字查询三个条件bool查询对象
            BoolQuery.Builder keywordBoolQueryBuilder = new BoolQuery.Builder();
            //2.1.2 分别设置查询专辑标题
            keywordBoolQueryBuilder.should(s -> s.match(m -> m.field("albumTitle").query(keyword)));
            //2.1.3 分别设置查询专辑简介
            keywordBoolQueryBuilder.should(s -> s.match(m -> m.field("albumIntro").query(keyword)));
            //2.1.4 分别设置查询专辑主播名称
            keywordBoolQueryBuilder.should(s -> s.term(t -> t.field("announcerName").value(keyword)));
            allBoolQueryBuilder.must(keywordBoolQueryBuilder.build()._toQuery());
        }
        //2.2 设置分类过滤（1、2、3级分类ID过滤）
        Long category1Id = queryVo.getCategory1Id();
        if (category1Id != null) {
            allBoolQueryBuilder.filter(f -> f.term(t -> t.field("category1Id").value(category1Id)));
        }
        Long category2Id = queryVo.getCategory2Id();
        if (category2Id != null) {
            allBoolQueryBuilder.filter(f -> f.term(t -> t.field("category2Id").value(category2Id)));
        }
        Long category3Id = queryVo.getCategory3Id();
        if (category3Id != null) {
            allBoolQueryBuilder.filter(f -> f.term(t -> t.field("category3Id").value(category3Id)));
        }
        //2.3 设置专辑标签（标签ID，标签值ID）过滤
        List<String> attributeList = queryVo.getAttributeList();
        if (CollectionUtil.isNotEmpty(attributeList)) {
            //2.3.1 获取筛选标签ID，级标签值过滤条件集合，每遍历一组标签条件 构建一个nested查询对象
            for (String attribute : attributeList) {
                //标签条件形式  标签ID：标签值ID
                String[] split = attribute.split(":");
                if (split != null && split.length == 2) {
                    //2.3.2 创建nested查询对象
                    NestedQuery.Builder nestedQueryBuilder = new NestedQuery.Builder();
                    nestedQueryBuilder.path("attributeValueIndexList");
                    nestedQueryBuilder.query(q -> q.bool(
                            b -> b.must(m -> m.term(t -> t.field("attributeValueIndexList.attributeId").value(split[0])))
                                    .must(m -> m.term(t -> t.field("attributeValueIndexList.valueId").value(split[1])))
                    ));
                    //2.3.3 在nested查询内部封装bool查询，包含标签ID，跟标签值ID
                    allBoolQueryBuilder.filter(nestedQueryBuilder.build()._toQuery());
                }
            }
        }
        //2.4 将封装所有查询条件放入请求体对象
        searchRequestBuilder.query(allBoolQueryBuilder.build()._toQuery());

        //3.设置分页，设置请求体参数"from"=(页码-1)*pageSize,"size"
        int from = (queryVo.getPageNo() - 1) * queryVo.getPageSize();
        searchRequestBuilder.from(from).size(queryVo.getPageSize());

        //4.设置排序 设置请求体参数中"sort"
        String order = queryVo.getOrder();
        //排序条件：排序（综合排序[1:desc] 播放量[2:desc] 发布时间[3:desc]；asc:升序 desc:降序）
        if (StringUtils.isNotBlank(order)) {
            String[] split = order.split(":");
            String orderField = "";
            if (split != null && split.length == 2) {
                //4.1 确定排序字段
                switch (split[0]) {
                    case "1":
                        orderField = "hotScore";
                        break;
                    case "2":
                        orderField = "playStatNum";
                        break;
                    case "3":
                        orderField = "createTime";
                        break;
                }
                String finalOrderField = orderField;
                searchRequestBuilder.sort(s -> s.field(f -> f.field(finalOrderField)
                        .order("asc".equals(split[1]) ? SortOrder.Asc : SortOrder.Desc)));
            }
        }

        //5.设置高亮，设置请求体参数中"highlight"
        if (StringUtils.isNotBlank(queryVo.getKeyword())) {
            searchRequestBuilder.highlight(h -> h.fields("albumTitle", h1 -> h1.preTags("<font style='color:red'>").postTags("</font>")));
        }

        //6.设置字段响应，设置请求体参数中"_source"
        List<String> excludeFields = Arrays.asList("attributeValueIndexList.attributeId",
                "attributeValueIndexList.valueId",
                "category1Id",
                "category2Id",
                "category3Id",
                "hotScore");
        searchRequestBuilder.source(s -> s.filter(f -> f.excludes(excludeFields)));

        return searchRequestBuilder.build();
    }


    /**
     * 解析ES检索结果
     *
     * @param searchResponse ES检索结果对象
     * @param queryVo
     * @return
     */
    @Override
    public AlbumSearchResponseVo parseResult(SearchResponse<AlbumInfoIndex> searchResponse, AlbumIndexQuery queryVo) {
        //1.创建响应结果VO对象
        AlbumSearchResponseVo vo = new AlbumSearchResponseVo();
        //2.封装VO对象中分页属性
        //2.1 封装分页 页码 页大小
        Integer pageNo = queryVo.getPageNo();
        Integer pageSize = queryVo.getPageSize();
        vo.setPageNo(pageNo);
        vo.setPageSize(pageSize);
        //2.2 获取总记录数
        long total = searchResponse.hits().total().value();
        vo.setTotal(total);
        //2.3 计算总页数
        long totalPages = total % pageSize == 0 ? total / pageSize : total / pageSize + 1;
        vo.setTotalPages(totalPages);

        //3.封装VO对象中当前页数据集合属性
        List<Hit<AlbumInfoIndex>> hits = searchResponse.hits().hits();
        if (CollectionUtil.isNotEmpty(hits)) {
            List<AlbumInfoIndexVo> albumInfoIndexVoList = hits.stream().map(hit -> {
                //3.1 获取命中专辑文档
                AlbumInfoIndex albumInfoIndex = hit.source();
                //3.2 处理可能存在高亮片段
                Map<String, List<String>> highlight = hit.highlight();
                if (CollectionUtil.isNotEmpty(highlight) && highlight.containsKey("albumTitle")) {
                    //3.3 获取专辑字段对应高亮片段
                    String highlightAlbumTitle = highlight.get("albumTitle").get(0);
                    albumInfoIndex.setAlbumTitle(highlightAlbumTitle);
                }
                //3.4 将查询到AlbumInfoIndex转为AlbumInfoIndexVo类型
                return BeanUtil.copyProperties(albumInfoIndex, AlbumInfoIndexVo.class);
            }).collect(Collectors.toList());
            vo.setList(albumInfoIndexVoList);
        }
        return vo;
    }

    /**
     * 查询指定一级分类下前7个三级分类，得到每个三级分类下热度前6的热门专辑
     *
     * @param category1Id 1级分类ID
     * @return
     */
    @Override
    public List<Map<String, Object>> searchCategory3Top6(Long category1Id) {
        try {
            //1. 处理及封装三级分类
            //1.1.远程调用专辑服务-得到1级分类下前7个三级分类集合
            List<BaseCategory3> baseCategory3List = albumFeignClient.getTopBaseCategory3(category1Id).getData();
            Assert.notNull(baseCategory3List, "三级分类为空");

            //1.2.获取三级分类集合中三级分类ID
            List<Long> category3IdList = baseCategory3List.stream().map(BaseCategory3::getId).collect(Collectors.toList());

            //1.4 DSL语句多关键字精确查询需要FiledValue集合->将ID集合转为FiledValue集合
            List<FieldValue> fieldValueList = category3IdList.stream().map(category3Id -> FieldValue.of(category3Id)).collect(Collectors.toList());

            //1.3 将三级分类集合转为Map 方便封装Map中分类对象 Key:三级分类ID Value：三级分类对象
            Map<Long, BaseCategory3> category3Map = baseCategory3List.stream().collect(Collectors.toMap(BaseCategory3::getId, baseCategory3 -> baseCategory3));


            //2.调用原生ElasticsearchClient进行检索-DSL参数较少采用lambda完成检索
            SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(
                    s -> s.index(INDEX_NAME)
                            .query(q -> q.terms(t -> t.field("category3Id").terms(t1 -> t1.value(fieldValueList))))
                            .size(0)
                            .aggregations("category3IdAgg", a -> a.terms(
                                            t -> t.field("category3Id").size(10)
                                    ).aggregations("hotScoreTop6", a1 -> a1.topHits(top -> top.size(6).sort(sort -> sort.field(f -> f.field("hotScore").order(SortOrder.Desc)))))
                            )
                    , AlbumInfoIndex.class
            );

            //3.解析ES响应结果，从聚合中获取每个三级分类下热度前6的专辑
            //3.1 获取三级分类ID聚合对象
            LongTermsAggregate category3IdAgg = searchResponse.aggregations().get("category3IdAgg").lterms();
            //3.2 获取三级分类ID聚合结果桶数组
            List<LongTermsBucket> category3Buckets = category3IdAgg.buckets().array();
            if (CollectionUtil.isNotEmpty(category3Buckets)) {
                //3.3 每遍历一个三级分类Bucket 处理该三级分类下热门专辑
                List<Map<String, Object>> mapList = category3Buckets.stream().map(category3Bucket -> {
                    //3.3.1 获取到三级分类ID
                    long category3Id = category3Bucket.key();
                    //3.3.2 通过三级分类集合对象获取子聚合
                    List<Hit<JsonData>> hotScoreTop6 = category3Bucket.aggregations().get("hotScoreTop6").topHits().hits().hits();
                    //3.3.3 当前分类下热门专辑
                    List<AlbumInfoIndex> category3Top6List = hotScoreTop6.stream().map(hit -> {
                        String albumInfoJSON = hit.source().toJson().toString();
                        return JSON.parseObject(albumInfoJSON, AlbumInfoIndex.class);
                    }).collect(Collectors.toList());
                    //3.4 封装当前三级分类下 热门对象Map
                    Map<String, Object> map = new HashMap<>();
                    map.put("baseCategory3", category3Map.get(category3Id));
                    map.put("list", category3Top6List);
                    return map;
                }).collect(Collectors.toList());
                return mapList;
            }
            return null;
        } catch (Exception e) {
            log.error("[搜索服务]分类下热门专辑异常：{}", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 根据已填写关键字返回自动补全内容
     *
     * @param keyword
     * @return
     */
    @Override
    public List<String> completeSuggest(String keyword) {
        try {
            //1.创建检索请求对象
            SearchRequest.Builder builder = new SearchRequest.Builder();
            builder.index("suggestinfo");
            builder.suggest(
                    s -> s.suggesters("keywordSuggest", f -> f.prefix(keyword).completion(c -> c.field("keyword").skipDuplicates(true).size(10)))
                            .suggesters("pinYinSuggest", f -> f.prefix(keyword).completion(c -> c.field("keywordPinyin").skipDuplicates(true).size(10).fuzzy(fu -> fu.fuzziness("2"))))
                            .suggesters("sequenceSuggest", f -> f.prefix(keyword).completion(c -> c.field("keywordSequence").skipDuplicates(true).size(10).fuzzy(fu -> fu.fuzziness("2"))))
            );
            SearchRequest searchRequest = builder.build();
            System.out.println("本次建议词DSL：");
            System.err.println(searchRequest);
            //2.执行建议词检索
            SearchResponse<SuggestIndex> searchResponse = elasticsearchClient.search(searchRequest, SuggestIndex.class);

            //3.解析ES响应数据，处理建议结果
            //3.1 准备可以自动去重重复提词集合HashSet
            Set<String> hashSet = new HashSet<>();
            hashSet.addAll(this.parseSugggestResult(searchResponse, "keywordSuggest"));
            hashSet.addAll(this.parseSugggestResult(searchResponse, "pinYinSuggest"));
            hashSet.addAll(this.parseSugggestResult(searchResponse, "sequenceSuggest"));
            if (hashSet.size() > 10) {
                return new ArrayList<>(hashSet).subList(0, 10);
            }
            //3.2 判断解析完成后集合长度如果小于10 根据关键词进行全文匹配查询
            if (hashSet.size() < 10) {
                SearchResponse<SuggestIndex> response = elasticsearchClient.search(s -> s.index("suggestinfo").query(q -> q.match(m -> m.field("title").query(keyword))), SuggestIndex.class);
                List<Hit<SuggestIndex>> hits = response.hits().hits();
                if (CollectionUtil.isNotEmpty(hits)) {
                    for (Hit<SuggestIndex> hit : hits) {
                        SuggestIndex suggestIndex = hit.source();
                        hashSet.add(suggestIndex.getTitle());
                        if (hashSet.size() >= 10) {
                            break;
                        }
                    }
                }
            }
            return new ArrayList<>(hashSet);
        } catch (Exception e) {
            log.error("[搜索服务]关键字自动补全异常：{}", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 解析建议响应结果
     *
     * @param searchResponse 检索响应对象
     * @param suggestName    自定义建议名称
     * @return
     */
    @Override
    public Collection<String> parseSugggestResult(SearchResponse<SuggestIndex> searchResponse, String suggestName) {
        List<String> list = new ArrayList<>();
        //1.获取指定建议结果
        List<Suggestion<SuggestIndex>> suggestionList = searchResponse.suggest().get(suggestName);
        if (CollectionUtil.isNotEmpty(suggestionList)) {
            for (Suggestion<SuggestIndex> suggestIndexSuggestion : suggestionList) {
                List<CompletionSuggestOption<SuggestIndex>> options = suggestIndexSuggestion.completion().options();
                if (CollectionUtil.isNotEmpty(options)) {
                    for (CompletionSuggestOption<SuggestIndex> option : options) {
                        SuggestIndex suggestIndex = option.source();
                        list.add(suggestIndex.getTitle());
                    }
                }
            }
        }
        return list;
    }

    /**
     * 查询ES中不同分类下热门专辑列表，将不同分类下不同排序热门专辑放入Redis
     */
    @Override
    public void updateLatelyAlbumRanking() {
        try {
            //1.远程调用专辑服务获取所有一级分类列表
            List<BaseCategory1> category1List = albumFeignClient.getAllCategory().getData();
            Assert.notNull(category1List, "一级分类集合为空");

            //2.遍历一级分类列表，每遍历一次查询当前一级分类下，不同排序方式 热门专辑列表
            for (BaseCategory1 baseCategory1 : category1List) {
                Long category1Id = baseCategory1.getId();
                //2.1 调用ES进行检索索引库
                String[] rankingDimensionArray = new String[]{"hotScore", "playStatNum", "subscribeStatNum", "buyStatNum", "commentStatNum"};
                for (String rankingDimension : rankingDimensionArray) {
                    //2.2 遍历排序字段，没处理一个排序字段 得到某个分类下某个排序字段对应热门专辑
                    SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(
                            s -> s.index(INDEX_NAME)
                                    .query(q -> q.term(t -> t.field("category1Id").value(category1Id)))
                                    .size(20)
                                    .sort(sf -> sf.field(f -> f.field(rankingDimension).order(SortOrder.Desc)))

                            , AlbumInfoIndex.class
                    );
                    //2.3 解析获取热门专辑列表
                    List<Hit<AlbumInfoIndex>> hits = searchResponse.hits().hits();
                    if (CollectionUtil.isNotEmpty(hits)) {
                        //3.将热门专辑存入Redis
                        List<AlbumInfoIndex> infoIndexList = hits.stream().map(hit -> hit.source()).collect(Collectors.toList());
                        //2.4 将当前分类下指定排序字段热门专辑放入Redis Hash结构中
                        //2.4.1 构建Redis排行数据Hash结构Key
                        String rangingKey = RedisConstant.RANKING_KEY_PREFIX + category1Id;
                        //2.4.2 构建REdis中Hash中field
                        String fieldKey = rankingDimension;
                        //2.4.3 存入Hash中
                        redisTemplate.opsForHash().put(rangingKey, fieldKey, infoIndexList);
                    }
                }
            }
        } catch (Exception e) {
            log.error("[专辑服务]导入热门专辑ES异常：{}", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 根据分类ID及热度方式查询专辑列表
     *
     * @param category1Id
     * @param dimension
     * @return
     */
    @Override
    public List<AlbumInfoIndex> getRankingList(Long category1Id, String dimension) {
        //1.构建热度hash结构的Key
        String rankingKey = RedisConstant.RANKING_KEY_PREFIX + category1Id;
        //2.构建排序字段hashKey
        List<AlbumInfoIndex> list = (List<AlbumInfoIndex>) redisTemplate.opsForHash().get(rankingKey, dimension);
        return list;
    }
}
